package com.lenovo.spider.common.spi;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassUtil;

import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author <a href="mailto:liuhan3@lenovo.com">HanL(liuhan3)</a>
 * on 2018/4/11 16:06
 */
public class ServiceScanner {
    private static final String PREFIX = "META-INF/services/";
    private static final Class<ServiceInterface> INTERFACE_ANNOTATION = ServiceInterface.class;
    private static final Class<ServiceProvider> IMPL_ANNOTATION = ServiceProvider.class;

    private final String basePackage;
    private Map<Class<?>, Collection<Class<?>>> serviceImplMap;
    private Map<String, Collection<String>> serviceImplStrMap;

    public ServiceScanner() {
        this(null);
    }

    public ServiceScanner(String basePackage) {
        this.basePackage = basePackage;
    }

    /**
     * 扫描并更新 META-INF/services
     */
    public void refresh() {
        scan();
        store();
    }

    /**
     * 加载配置文件并更新 META-INF/services
     * @param file
     */
    public void load(String file) {

    }

    private void scan() {
        Set<Class<?>> interfaces = ClassUtil.scanPackageByAnnotation(basePackage, INTERFACE_ANNOTATION);
        Set<Class<?>> impls = ClassUtil.scanPackageByAnnotation(basePackage, IMPL_ANNOTATION);

        if (interfaces.size() == 0) return;

        serviceImplMap = MapUtil.newHashMap(interfaces.size());
        for (Class<?> anInterface : interfaces) {
            serviceImplMap.put(anInterface, CollUtil.filter(impls, anInterface::isAssignableFrom));
        }
    }

    private void store() {
        if (CollUtil.isEmpty(serviceImplMap)) {
            return;
        }

        serviceImplMap.forEach(this::saveServiceProvider);
    }

    private void saveServiceProvider(Class<?> anInterface, Collection<Class<?>> impls) {
        String fileName = anInterface.getName();
        Collection<String> body = impls.stream()
                .sorted(this::getComparator)
                .map(Class::getName)
                .collect(Collectors.toList());
        FileUtil.writeLines(body, PREFIX.concat(fileName), Charset.forName("utf-8"));
    }

    private int getComparator(Class<?> aClass, Class<?> bClass) {
        int o1 = aClass.getAnnotation(IMPL_ANNOTATION).order();
        int o2 = bClass.getAnnotation(IMPL_ANNOTATION).order();
        return o1 != o2 ? o1 - o2 : aClass.getName().compareTo(bClass.getName());
    }

}
